# -*- coding = utf-8 -*-
# name   : 删除历史数据
# Author : admin
# Email  :
# File   : delete_historical_data.py
# Ver    : 1.0
# Time   : 2025-07-14 11:51
import os
import time
import logging

from datetime import datetime, timedelta
from apscheduler.schedulers.blocking import BlockingScheduler

from config import current_env
from utils.operate_mongodb import OperateMongodb

fmt = '%(asctime)s %(levelname)s: %(message)s - %(filename)s[line:%(lineno)d]'
datefmt = '%Y-%m-%d %H:%M:%S'

logging.basicConfig(level=logging.INFO,
                    datefmt=datefmt,
                    format=fmt)

# 日期时间格式
TIME_FORMAT = '%Y-%m-%d'


# === 函数耗时统计 装饰器
def timer(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"函数 {func.__name__} 耗时 {end_time - start_time:.2f} 秒")
        return result

    return wrapper


# 写入日志到文件并自动管理文件大小
def write_log(message, log_file='app_tips.log', max_size=104857600, backup_count=3):
    """
    写入日志到文件并自动管理文件大小

    参数：
    message: 要写入的日志内容（字符串）
    log_file: 日志文件路径（默认：当前目录下的app.log）
    max_size: 最大文件大小（字节，默认100MB=104857600字节）
    backup_count: 最大备份文件数量（默认保留3个备份）
    """
    # 定义日志文件路径
    log_dir = "logs"
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)  # 创建日志目录
    log_file = os.path.join(log_dir, log_file)  # 合并日志目录和文件名

    # 检查日志文件是否存在且是否超过大小限制
    if os.path.exists(log_file):
        file_size = os.path.getsize(log_file)  # 获取当前文件大小
        if file_size > max_size:  # 超过阈值时执行备份操作
            # 从最新的备份开始向后轮转（保持备份数量不超过指定值）
            # 例如 backup_count=3 时，循环范围是 2,1（因为range是左闭右开）
            for i in range(backup_count - 1, 0, -1):
                src = f"{log_file}.{i}"  # 当前备份文件路径（如 app.log.2）
                # 计算下一个备份位置（最后一个备份直接命名为.bak）
                dst = f"{log_file}.{i + 1}" if i != backup_count - 1 else f"{log_file}.bak"

                # 新增安全处理：如果目标文件存在则先删除
                if os.path.exists(dst):
                    os.remove(dst)  # 删除可能存在的旧备份

                # 如果源文件存在则执行重命名（避免覆盖旧备份）
                if os.path.exists(src):
                    os.rename(src, dst)

            # 处理第一个备份位置（将当前文件备份为.1）
            # 先删除可能存在的旧.1文件（防止残留）
            if os.path.exists(f"{log_file}.1"):
                os.remove(f"{log_file}.1")
            # 将当前日志文件重命名为.1备份
            os.rename(log_file, f"{log_file}.1")

    # 以追加模式打开日志文件（保证线程安全）
    with open(log_file, 'a', encoding='utf-8') as f:
        # 获取格式化后的当前时间戳
        timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
        # 写入格式化后的日志条目（包含时间戳）
        f.write(f"[{timestamp}] {message}\n")


# 删除指定集合中指定时间之前的数据
@timer
def delete_historical_data_butongdb(db_mongo, collection_name, days=7, date_lie: str = 'create_time', task_name=''):
    """
    删除指定集合中指定时间之前的数据
    :param db_mongo: 数据库连接
    :param collection_name: 集合名称
    :param days: 时间间隔，单位：天
    :param date_lie: 时间字段
    :return:
    """
    query = get_day_to_delete(days, date_lie)

    sql_sql = F"db.{collection_name}.find({query}).count()"

    print(sql_sql)

    res = db_mongo[collection_name].delete_many(query)
    # 删除条数
    delete_count = res.deleted_count

    # 聚合查询
    aggregate_query = [
        {
            "$group": {
                "_id": "null",
                "count": {
                    "$sum": 1
                }
            }
        }
    ]

    res1 = list(db_mongo[collection_name].aggregate(aggregate_query))
    # 剩余数据条数
    res_count = 0
    if len(res1) > 0:
        # 剩余数据条数
        res_count = list(res1)[0]['count']

    msg = f"[{task_name}] {collection_name} 集合删除[{days}]天之前的数据[{delete_count}]条；剩余[{res_count}]条数据！"
    logging.info(msg)
    write_log(msg)


# 删除指定集合中指定时间区间的数据
def delete_historical_data_by_time_range(db_mongo, collection_name, start_time, end_time,
                                         date_lie: str = 'create_time'):
    """
    删除指定集合中指定时间区间的数据
    :param db_mongo: 数据库连接
    :param collection_name: 集合名称
    :param start_time: 开始时间
    :param end_time: 结束时间
    :param date_lie: 时间字段
    :return:
    """
    # 删除指定时间区间的数据
    res = db_mongo[collection_name].delete_many({date_lie: {"$lt": end_time, "$gte": start_time}})
    logging.info(f"删除{collection_name}集合中{start_time}到{end_time}之间的数据[{res.deleted_count}]条成功！")
    write_log(f"删除{collection_name}集合中{start_time}到{end_time}之间的数据[{res.deleted_count}]条成功！")


# 日志类数据删除
def log_task_start_end(db_mongo, task_name, table_names):
    # 日志类数据删除
    logging.info(f"**{task_name} 历史数据清除任务** start")

    for table_name, days, date_lie in table_names:
        delete_historical_data_butongdb(db_mongo, table_name, days, date_lie, task_name)


#  跑批大屏 项目历史数据清除任务
def paopi_task():
    from addon.delete_historical_data.table_name import paopi_table_names as table_names
    from config import MONGODB_CONF
    mongo_object = OperateMongodb(MONGODB_CONF)
    _, db_mongo = mongo_object.conn_mongodb()

    log_task_start_end(db_mongo, "跑批大屏", table_names)

    mongo_object.close()


# 桂银查查 历史数据清除任务
def gycc_task():
    from addon.delete_historical_data.table_name import gycc_table_names as table_names
    from config import MONGODB_CONF_GYCC as MONGODB_CONF
    mongo_object = OperateMongodb(MONGODB_CONF)
    _, db_mongo = mongo_object.conn_mongodb()

    log_task_start_end(db_mongo, "桂银查查", table_names)

    mongo_object.close()


# 外管项目历史数据清除任务
def wg_task():
    from addon.delete_historical_data.table_name import wg_table_names as table_names
    from config import MONGODB_CONF_WG as MONGODB_CONF
    mongo_object = OperateMongodb(MONGODB_CONF)
    _, db_mongo = mongo_object.conn_mongodb()

    log_task_start_end(db_mongo, "外管大屏", table_names)

    mongo_object.close()


# 定时任务
def main():
    if current_env in ['production']:
        logging.info("**生产环境 定时任务 执行** start")
        scheduler = BlockingScheduler(timezone="Asia/Shanghai")

        # 每天凌晨2点30分执行一次
        scheduler.add_job(paopi_task, 'cron', hour=2, minute=30, next_run_time=(datetime.now() + timedelta(seconds=5)),
                          jitter=3)
        scheduler.add_job(gycc_task, 'cron', hour=2, minute=33, next_run_time=(datetime.now() + timedelta(seconds=15)),
                          jitter=3)
        scheduler.add_job(wg_task, 'cron', hour=2, minute=36, next_run_time=(datetime.now() + timedelta(seconds=25)),
                          jitter=3)

        # 启动任务
        scheduler.start()
    else:
        logging.info("**开发环境 直接执行** start")
        paopi_task()
        gycc_task()
        wg_task()


def get_day_to_delete(days=7, date_lie: str = 'create_time'):
    day_to_delete = (datetime.now() - timedelta(days=days)).strftime(TIME_FORMAT)
    day_to_delete_compact = day_to_delete.replace('-', '')

    delete_query = {
        date_lie: {
            "$regex": F"^(?:{day_to_delete}|{day_to_delete_compact})"
        }
    }
    return delete_query


if __name__ == '__main__':
    # query_str = get_day_to_delete(days=7)
    #
    # sql_str = F"db.lyj_esb_res.find({query_str})"
    # sql_str1 = F"db.lyj_esb_res.find({query_str}).count()"
    # print(sql_str)
    # print(sql_str1)

    paopi_task()
    gycc_task()
    wg_task()